package cc.ccoke.validate.verify;

import cc.ccoke.validate.annotation.ParamType;
import cc.ccoke.validate.entity.Regular;
import cc.ccoke.validate.exception.ParamAnnotationException;
import cc.ccoke.validate.exception.ParamRegularException;
import cc.ccoke.validate.factory.RegularFactory;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Optional;
import java.util.regex.Pattern;

/**
 * 验证基类
 * @author ccoke
 */
public abstract class BaseParamVerify {
    /**
     * 基本类型、包装类型
     */
    private static final String[] TYPES = {"java.lang.Integer",
            "java.lang.Double",
            "java.lang.Float",
            "java.lang.Long",
            "java.lang.Short",
            "java.lang.Byte",
            "java.lang.Boolean",
            "java.lang.Character",
            "java.lang.String",
            "int","double","long","short","byte","boolean","char","float"};

    /**
     * 判断元素是否在数组中
     * @param item 被测元素
     * @param array 比较数组
     * @return true 存在; false 不存在
     */
    protected boolean isItemInArray(Object item, Object[] array){
        for (int i = 0; i < array.length; i++) {
            if (item.equals(array[i])){
                return true;
            }
        }
        return false;
    }

    /**
     * 判断类型是否为基本类型
     * @param type 被判断类型
     * @return true 基本类型; false 非基本类型
     */
    protected boolean isBasicType(String type){
        return isItemInArray(type, TYPES);
    }

    /**
     * 判断对象是否为空，如果是String类型还要判断是否为空字符串
     * @param obj 被测对象
     * @return true 为空 ; false 不为空
     */
    protected boolean isEmpty(Object obj){
        return  obj == null || (obj instanceof String && "".equals(obj));
    }

    /**
     * 对方法进行参数验证
     * @param args 方法中的参数
     * @param methodSignature 目标方法签名
     * @param value ParamVerify注解中的value
     * @throws IllegalAccessException 反射获取参数异常
     */
    public abstract void doVerify(Object[] args, MethodSignature methodSignature, String[] value) throws IllegalAccessException;

    /**
     * 判断基本对象是否满足规则
     * @param obj 被测的基本对象
     * @param regex 正则表达式
     * @return true 满足规则; false 不满足规则
     */
    private boolean isMatchPattern(Object obj, String regex){
        if (obj == null) {
            return false;
        }
        if( !isBasicType(obj.getClass().getTypeName())) {
            throw new ParamAnnotationException("Target param must be Basic type");
        }
        return Pattern.matches(regex, String.valueOf(obj));
    }

    /**
     * 检验方法中参数规则
     * @param annotations 方法上所有注解
     * @param index 当前对象下标
     * @param arg 当前对象
     * @param paramName 当前对象名称
     * @throws  ParamAnnotationException 注解中value错误
     * @throws ParamRegularException 目标参数不符合规则
     */
    protected void checkParamType(Annotation[][] annotations, int index, Object arg, String paramName) throws ParamAnnotationException, ParamRegularException {
        for (int j = 0; j < annotations[index].length; j++) {
            Annotation annotation = annotations[index][j];
            if(annotation.annotationType() == ParamType.class){
                checkParamType((ParamType) annotation, paramName, arg);
                break;
            }
        }
    }

    /**
     * 检验对象内属性规则
     * @param field 被测属性
     * @param arg 当前对象
     * @param paramName 当前对象名称
     * @throws ParamAnnotationException 注解中value错误
     * @throws ParamRegularException 目标参数不符合规则
     * @throws IllegalAccessException 反射获取参数异常
     */
    protected void checkParamType(Field field, Object arg, String paramName) throws IllegalAccessException, ParamAnnotationException, ParamRegularException {
        ParamType paramType = field.getAnnotation(ParamType.class);
        if (paramType == null) {
            return;
        }
        checkParamType(paramType, paramName, field.get(arg));
    }

    /**
     * 检查参数是否符合规则
     * @param paramType 参数校验注解
     * @param paramName 参数名称
     * @param var1 参数值
     */
    private void checkParamType(ParamType paramType, String paramName, Object var1) {
        String regex = paramType.regex();
        String describe = paramType.describe();
        if (!("".equals(regex))) {
            // 如果直接在paramType上自定义了规则，则使用该规则
            if (!isMatchPattern(var1, regex)){
                throw new ParamRegularException(paramName, "".equals(describe) ? paramType.regex() : describe);
            }
        }
        String key = paramType.value();
        if ("".equals(key)) {
            return;
        }
        Regular regular = RegularFactory.DEFAULT_REGULARS.get(key);
        if (regular == null) {
            throw new ParamAnnotationException(key + " not define!");
        }
        if (!isMatchPattern(var1, regular.getRegex())){
            throw new ParamRegularException(paramName, "".equals(describe) ? regular.getDescribe() : describe);
        }
    }
}
